package com.acme.domain;

import java.util.Comparator;
import java.util.HashSet;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.acme.domain.listener.MarketDataListener;
import com.acme.domain.listener.OrderStateListener;
import com.acme.domain.request.Request;


public class Stock implements Exchange{
	
	private String stockName;
		
	private Comparator<Order> orderComparator = new StockComparator();
    private SortedSet<Order> orders = new TreeSet<Order>(orderComparator);
	
	private Set<MarketDataListener> marketDataListeners = new HashSet<MarketDataListener>();
	private Set<OrderStateListener> orderStateListeners = new HashSet<OrderStateListener>();
	
	private ExecutorService stockThread = Executors.newSingleThreadExecutor();
	
	int currentID;
	final int ID_RANGE = 10;
	

	public Stock(final String name) {
		this.stockName = name;
		currentID = 0;
	}

	@Override
	public void placeOrder(Order o) {
		orders.add(o);
		notifyMarketDataListeners(o);
		notifyOrderStateListeners(o);
	}

	@Override
	public boolean cancelOrder(Order o) {
		boolean op = false;
		try {
			op = orders.remove(o);
		} catch (NullPointerException ex){
			throw new NullPointerException("Exchange " + stockName + " doesn't " +
					"contain order with id " + o.getId() + " to cancel");
		}
		if (op){
			notifyMarketDataListeners(o);
			notifyOrderStateListeners(o);
		}
		return op;
	}

	public Order idSearch(int id) {
		for (Order o : orders) {
			if (o.getId() == id){
				return o;
			}
		}
		return null;
	}

	@Override
	public void subscribeMarketData(String clientName) {
		marketDataListeners.add(new MarketDataListener(clientName));
	}

	@Override
	public void unsubscribeMarkedData(String clientName) {
		marketDataListeners.remove(new MarketDataListener(clientName));
	}

	@Override
	public void subscribeOrderState(String clientName) {
		orderStateListeners.add(new OrderStateListener(clientName));
	}

	@Override
	public void unsubscribeOrderState(String clientName) {
		orderStateListeners.remove(new OrderStateListener(clientName));
	}

	public int getCurrentID() {
		return currentID;
	}
	
	public void setCurrentID(int currentID) {
		this.currentID = currentID;
	}
	
	public int getID_RANGE() {
		return ID_RANGE;
	}
	
	private void notifyMarketDataListeners(Order o){
		for (MarketDataListener listener : marketDataListeners){
			listener.updateInfo(o);
		}
	}
	
	private void notifyOrderStateListeners(Order o){
		for (OrderStateListener listener : orderStateListeners){
			listener.updateInfo(o);
		}
	}

	public SortedSet<Order> getOrders() {
		return orders;
	}
	
	public void execute(Request request){
		stockThread.execute( new StockThread(this, request));
	}

	public String getStockName() {
		return stockName;
	}
	
    public void shutdown(){
    	stockThread.shutdown();
    }
}

class StockComparator implements Comparator<Order>{
    @Override 
    public int compare(Order o1, Order o2) {
    	if ( o1.getOpperationType() == Operationtype.SELL && 
    			o2.getOpperationType() == Operationtype.BUY){
    		return 1;
    	} else if (o1.getOpperationType() == Operationtype.BUY &&
    			o2.getOpperationType() == Operationtype.SELL){
    		return -1;
    	} else if( o1.getOpperationType() == Operationtype.SELL &&
    			o2.getOpperationType() == Operationtype.SELL){
    		return -priceAndDateComp(o1, o2);
    	} else{
    		return priceAndDateComp(o1, o2);
    	}
    }
    
    private int priceAndDateComp(Order o1, Order o2){
       	int priceComp = (int) ( o1.getPrice()-o2.getPrice() );
    	if ( priceComp == 0){
    		int dateComp = o1.getDate().compareTo(o2.getDate());
    		if ( dateComp == 0) 
    			return o1.getId()-o2.getId();
    		else {
    			return dateComp;
    		}
    	} else {
    		return priceComp;
    	}
    }
    

}